package com.bright.ghj.overt.task.timely;

import com.bright.ghj.common.util.ApplicationContextProvider;
import com.bright.ghj.common.util.DateUtil;
import com.bright.ghj.overt.util.SecurityUtil;
import com.bright.ghj.common.util.StringUtil;
import com.bright.ghj.overt.pojo.dto.AsyncMessageDTO;
import com.bright.ghj.overt.service.AsyncMessageService;
import com.bright.ghj.overt.service.impl.AsyncMessageServiceImpl;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.util.Enumeration;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

import static com.bright.ghj.overt.enums.AsyncTaskExecInfo.*;

/**
 * @author Tz
 * @date 2024/01/22 15:03
 * @Description 异步任务执行器的抽象类 继承AsyncMessageDTO 获得执行情况的状态
 */
@Slf4j
@Data
@NoArgsConstructor
@EqualsAndHashCode(callSuper = true)
public abstract class AsyncTaskExecutor extends AsyncMessageDTO implements Serializable {

    /**
     * 异步任务执行器名称
     */
    private String asyncTaskExecutorName;

    /**
     * 原子操作
     * <p>
     * key:   实际的异步任务执行器名称
     * value: 实际的异步任务同步锁集合
     */
    private static ConcurrentHashMap<String, ConcurrentHashMap> BASIC_TASK_LOCK = new ConcurrentHashMap<>();

    /**
     * 进度百分比
     */
    protected static final Integer PERCENTAGE  = 100;

    /**
     * 完成度
     */
    protected Integer completionDegree;

    /**
     * 进度步长
     */
    protected Integer progressStepSize;

    /**
     * 任务是否是运行状态
     */
    protected Boolean isRunning = true;

    /**
     * 同步锁的关键值
     * <p>
     * 相同的key值会进行同步操作
     * <p>
     * 注意: 不设置也是同步执行的
     */
    protected String syncKeyword = "";

    /**
     * 处理异步消息任务的接口
     */
    protected AsyncMessageService asyncMessageService;

    /**
     * 初始化值
     */
    public AsyncTaskExecutor(String asyncTaskExecutorName){
        //======= 初始化相关参数 =======
        //初始进度
        setCompletionDegree(0);
        //默认未读状态
        setReadFlag(false);
        //默认待执行状态
        setExecStatus(TODO_EXEC.getStatus());
        //消息默认可以用
        setRunFlag(true);
        //消息默认未开始执行
        setExecFlag(false);
        setCreatedBy(SecurityUtil.getLoginUser().getUsername());
        setCreatedTime(DateUtil.getCurrDate());

        this.asyncTaskExecutorName = asyncTaskExecutorName;
        this.asyncMessageService = ApplicationContextProvider.getApplicationContext().getBean(AsyncMessageServiceImpl.class);

    }

    /**
     * 基础执行器，只是做异步任务的消息记录 具体的业务由子类实现 {@link #taskContent()} 完成
     */
    public void baseExec() {

        AsyncMessageDTO asyncMessageDTO = asyncMessageService.getById(this.getId());
        //记录时间
        long startTime = System.currentTimeMillis();

        try {

            //锁对象
            Object object = null;

            if (!BASIC_TASK_LOCK.containsKey(asyncTaskExecutorName)) {
                BASIC_TASK_LOCK.put(asyncTaskExecutorName, new ConcurrentHashMap<String, Object>());
            }

            //1、校验任务是否取消
            //需要二次判断，处理锁
            synchronized (this){
                //查看这条消息是否是未执行且可用的
                AsyncMessageDTO messageDTO = asyncMessageService.getById(asyncMessageDTO.getId());

                //如果不可用 代表该消息在没被消费前被取消了
                if(!messageDTO.getRunFlag()){
                    messageDTO.setContentInfo("任务被取消！");
                    messageDTO.setExecStatus(EXECUTION_CANCEL.getStatus());
                    messageDTO.setExecFlag(true);
                    messageDTO.setUpdatedBy(asyncMessageDTO.getUsername());
                    messageDTO.setUpdatedTime(DateUtil.getCurrDate());
                    asyncMessageService.save(messageDTO);
                    return;
                }

                //如果根据key查询到了对象锁
                ConcurrentHashMap concurrentHashMap = BASIC_TASK_LOCK.get(asyncTaskExecutorName);
                Enumeration enumeration = concurrentHashMap.keys();
                while(enumeration.hasMoreElements()){
                    String key = (String) enumeration.nextElement();
                    if(key.equalsIgnoreCase(this.syncKeyword)){
                        object = concurrentHashMap.get(key);
                    }
                }

                //没有获取到锁则新增
                if(object == null){
                    object = new Object();
                }
            }

            //进入消费 表示该消息已经开始消费了
            //3、锁住自己定义的同步锁 进行业务处理
            //如果上一次执行的锁标识没有释放, 现在有需要没释放的锁，就会进行等待
            synchronized(object) {
                try {

                    //查看这条消息是否是未消费且可用的
                    AsyncMessageDTO resMqMessage = asyncMessageService.getById(asyncMessageDTO.getId());
                    //如果不可用 代表该消息在没被消费前被取消了
                    if(!resMqMessage.getRunFlag()){
                        resMqMessage.setContentInfo("任务被取消！");
                        resMqMessage.setExecStatus(EXECUTION_CANCEL.getStatus());
                        resMqMessage.setUpdatedBy(resMqMessage.getUsername());
                        resMqMessage.setUpdatedTime(DateUtil.getCurrDate());
                        asyncMessageService.save(resMqMessage);
                        return;
                    }

                    //开始执行 将锁标识放入
                    BASIC_TASK_LOCK.get(asyncTaskExecutorName).put(this.syncKeyword, object);
                    asyncMessageDTO.setExecFlag(true);
                    asyncMessageDTO.setExecStatus(EXECUTING.getStatus());
                    AsyncMessageDTO updResMessage = asyncMessageService.save(asyncMessageDTO);

                    if (Objects.isNull(updResMessage)) {
                        throw new RuntimeException("更新异步任务状态异常!");
                    }

                    //执行实际的任务内容
                    boolean execFlag = taskContent();

                    if (execFlag) {
                        //执行成功
                        asyncMessageDTO.setExecStatus(EXECUTION_SUCCESSFUL.getStatus());
                        asyncMessageDTO.setContentInfo(String.format("执行: %s 成功!", asyncMessageDTO.getTaskName()));
                    } else {
                        //执行失败
                        asyncMessageDTO.setExecStatus(EXECUTION_FAILED.getStatus());
                        asyncMessageDTO.setContentInfo(String.format("执行: %s 失败!", asyncMessageDTO.getTaskName()));
                        //设置执行失败的错误信息
                        asyncMessageDTO.setErrorInfo(this.getErrorInfo());
                    }

                } catch (Exception e){
                    // 将异常信息保存到String中
                    StringWriter sw = new StringWriter();
                    PrintWriter pw = new PrintWriter(sw);
                    e.printStackTrace(pw);
                    String exceptionAsString = sw.toString();

                    System.out.println(exceptionAsString);
                    //执行失败
                    asyncMessageDTO.setExecStatus(EXECUTION_ERROR.getStatus());
                    asyncMessageDTO.setContentInfo(String.format("执行: %s 失败!", asyncMessageDTO.getTaskName()));
                    asyncMessageDTO.setErrorInfo(exceptionAsString);
                } finally {
                    //4、释放当前执行地区持有的锁
                    BASIC_TASK_LOCK.get(asyncTaskExecutorName).remove(this.syncKeyword);
                }
            }

            long endTime = System.currentTimeMillis();
            asyncMessageDTO.setExecTime(endTime - startTime);
            asyncMessageDTO.setUpdatedBy(asyncMessageDTO.getUsername());
            asyncMessageDTO.setUpdatedTime(DateUtil.getCurrDate());
            asyncMessageService.save(asyncMessageDTO);

        } catch (Exception e) {
            // 将异常信息保存到String中
            StringWriter sw = new StringWriter();
            PrintWriter pw = new PrintWriter(sw);
            e.printStackTrace(pw);
            String exceptionAsString = sw.toString();

            //执行错误
            asyncMessageDTO.setExecStatus(EXECUTION_ERROR.getStatus());
            asyncMessageDTO.setContentInfo(e.getMessage());
            asyncMessageDTO.setErrorInfo(exceptionAsString);
            long endTime = System.currentTimeMillis();
            asyncMessageDTO.setExecTime(endTime - startTime);
            asyncMessageDTO.setUpdatedBy(asyncMessageDTO.getUsername());
            asyncMessageDTO.setUpdatedTime(DateUtil.getCurrDate());
            asyncMessageService.save(asyncMessageDTO);
        } finally {
            this.isRunning = false;
//            Long endTime = System.currentTimeMillis();
//            asyncMessageDTO.setExecTime(endTime - startTime);
//            asyncMessageDTO.setUpdatedBy(asyncMessageDTO.getUsername());
//            asyncMessageDTO.setUpdatedTime(DateUtil.getCurrDate());
//            asyncMessageService.save(asyncMessageDTO);
        }
    }


    /**
     * 实际异步执行的任务体抽象方法、由具体的异步任务实现
     * @return            执行结果
     * @throws Exception  可能出现的业务异常
     */
    public abstract boolean taskContent() throws Exception;

    /**
     * 推进进度
     */
    public void advanceProgress() {
        completionDegree = completionDegree + progressStepSize;
        if (completionDegree > PERCENTAGE) {
            completionDegree = PERCENTAGE;
        }
        log.info("当前任务keyword:[{}], 任务类型: [{}], 任务名称: [{}], 任务进度: [{}%], 是否正在运行: [{}]"
                , getKeyword(), getTaskType(), getTaskName(), getCompletionDegree(), getIsRunning() ? "是" : "否");
    }

    /**
     * 对执行器进行必要的校验
     * @return 校验结果
     */
    public boolean checkAsyncTaskExecutor() {
        if (StringUtil.isBlank(this.asyncTaskExecutorName)
                || StringUtil.isBlank(this.syncKeyword)) {
            return false;
        }

        return true;
    }

}
